﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ITAP.ShprossesBuilder.Calculations;
using System.Drawing;
using ITAP.Core.Modelling;
using ITAP.Core.Calculations;

namespace ITAP.ShprossesBuilder.Modelling.Components.Actions
{
    public enum DiamondMeasure
    {
        Width, 
        Height,
        DiamondBorder
    }

    public class ActionSelectedShprossConnectorsDiamondAlignment : IComponentAction
    {
        private ModelControl modelControl;
        private DiamondMeasure measure;
        private double distance;
        private RectangleF boundingBox;
        public ActionSelectedShprossConnectorsDiamondAlignment(ModelControl modelControl, double distanceBetweenShprossConnectors, DiamondMeasure distanceMeasure, RectangleF boundingBox)
        {
            this.modelControl = modelControl;
            this.distance = distanceBetweenShprossConnectors;
            this.measure = distanceMeasure;
            this.boundingBox = boundingBox;
        }

        #region IComponentAction Members

        public bool Action(IComponent component)
        {
            int shprossConnectorCounter = 0;
            List<IComponent> componentsToProceed = new List<IComponent>();

            // Находим первые четыре выделенных соединения, из которых будем составлять ромб.
            foreach (IComponent selectedComponent in modelControl.ModelContainer.SelectedComponentsStack)
            {
                if (shprossConnectorCounter == 4)
                    break;

                if (selectedComponent is ShprossConnector)
                {
                    componentsToProceed.Add(selectedComponent);
                    shprossConnectorCounter++;
                }
            }

            // Находим центр будущего ромба.
            double minX = double.MaxValue;
            double maxX = double.MinValue;
            double minY = double.MaxValue;
            double maxY = double.MinValue;

            ShprossConnector right = null;
            ShprossConnector up = null;
            ShprossConnector left = null;
            ShprossConnector down = null;
            foreach (IComponent shprossConnector in componentsToProceed)
            {
                if (shprossConnector.Position.X < minX)
                {
                    minX = shprossConnector.Position.X;
                    left = shprossConnector as ShprossConnector;
                }
                if (shprossConnector.Position.X > maxX)
                {
                    maxX = shprossConnector.Position.X;
                    right = shprossConnector as ShprossConnector;
                }
                if (shprossConnector.Position.Y < minY)
                {
                    minY = shprossConnector.Position.Y;
                    down = shprossConnector as ShprossConnector;
                }
                if(shprossConnector.Position.Y > maxY)
                {
                    maxY = shprossConnector.Position.Y;
                    up = shprossConnector as ShprossConnector;
                }
            }

            ModelPoint center = new ModelPoint((minX + maxX) / 2, (minY + maxY) / 2);
            // Проверяем, можно ли составить из коннекторов ромб (проверяем углы).
            double totalAngle = 0;
            foreach (IComponent shprossConnector in componentsToProceed)
                totalAngle += this.GetAngleBetweenNearestConnectors((shprossConnector as ShprossConnector), center).Value;


            // Если из коннекторов невозможно составить ромб (сумма углов не равна 2 * Math.PI), ничего не удалось.
            if (!SimpleEqualProvider.IsEqual(totalAngle, 2 * Math.PI, 0.000001))
                return false;

            List<ShprossConnector> shprossConnectors = new List<ShprossConnector>();
            shprossConnectors.Add(right);
            shprossConnectors.Add(up);
            shprossConnectors.Add(left);
            shprossConnectors.Add(down);

            // Находим 
            switch(measure)
            {
                case DiamondMeasure.Width:
                    this.WidthAlign(shprossConnectors, center, distance);
                    break;
                case DiamondMeasure.Height:
                    this.HeightAlign(shprossConnectors, center, distance);
                    break;
                case DiamondMeasure.DiamondBorder:
                    this.DiamondBorderAlign(shprossConnectors, center, distance);
                    break;
            }
            return true;
        }

        #endregion

        private void DiamondBorderAlign(List<ShprossConnector> shprossConnectors, ModelPoint center, double distance)
        {
            Angle alpha = this.GetAngleBetweenNearestConnectors(shprossConnectors[1], center);
            Angle beta = this.GetAngleBetweenNearestConnectors(shprossConnectors[0], center);
            double xOffset = distance * Math.Cos(beta.Value / 2);
            double yOffset = distance * Math.Cos(alpha.Value / 2);
            ModelVector offsetVector = new ModelVector(xOffset, 0);
            ModelPoint rightPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            offsetVector = offsetVector.Rotate(new Angle(Math.PI));
            ModelPoint leftPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            offsetVector = new ModelVector(0, yOffset);
            ModelPoint upPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            offsetVector = offsetVector.Rotate(new Angle(Math.PI));
            ModelPoint downPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);

            shprossConnectors[0].MoveTo(rightPoint);
            shprossConnectors[1].MoveTo(upPoint);
            shprossConnectors[2].MoveTo(leftPoint);
            shprossConnectors[3].MoveTo(downPoint);
        }

        private void WidthAlign(List<ShprossConnector> shprossConnectors, ModelPoint center, double distance)
        {
            ModelVector offsetVector = new ModelVector(1, 0).Multiply(distance / 2);
            ModelPoint rightPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            offsetVector = offsetVector.Rotate(new Angle(Math.PI));
            ModelPoint leftPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            Angle beta = this.GetAngleBetweenNearestConnectors(shprossConnectors[0], center);
            double height = distance / 2 * Math.Tan(beta.Value / 2);
            offsetVector = new ModelVector(0, height);
            ModelPoint upPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            offsetVector = offsetVector.Rotate(new Angle(Math.PI));
            ModelPoint downPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);

            shprossConnectors[0].MoveTo(rightPoint);
            shprossConnectors[1].MoveTo(upPoint);
            shprossConnectors[2].MoveTo(leftPoint);
            shprossConnectors[3].MoveTo(downPoint);
        }

        private void HeightAlign(List<ShprossConnector> shprossConnectors, ModelPoint center, double distance)
        {
            ModelVector offsetVector = new ModelVector(0, 1).Multiply(distance / 2);
            ModelPoint upPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            offsetVector = offsetVector.Rotate(new Angle(Math.PI));
            ModelPoint downPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            Angle alpha = this.GetAngleBetweenNearestConnectors(shprossConnectors[1], center);
            double width = distance / 2 * Math.Tan(alpha.Value / 2);
            offsetVector = new ModelVector(width, 0);
            ModelPoint rightPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            offsetVector = offsetVector.Rotate(new Angle(Math.PI));
            ModelPoint leftPoint = ModelCalculations.GetOffsetPoint(center, offsetVector);
            shprossConnectors[0].MoveTo(rightPoint);
            shprossConnectors[1].MoveTo(upPoint);
            shprossConnectors[2].MoveTo(leftPoint);
            shprossConnectors[3].MoveTo(downPoint);
        }

        private Angle GetAngleBetweenNearestConnectors(ShprossConnector shprossConnector, ModelPoint referencePoint)
        {
            Angle referencePointAngle = 
                ModelCalculations.GetAngleBetweenVectors(shprossConnector.DirectionVector, new ModelVector(shprossConnector.Position, referencePoint), false);
            for(int i = 0; i < shprossConnector.AllowedConnectorsAngles.Count; i++)
            {
                AngleInterval interval = shprossConnector.AllowedConnectorsAngles[i];
                AngleInterval nextInterval;
                if (i == shprossConnector.AllowedConnectorsAngles.Count - 1)
                    nextInterval = shprossConnector.AllowedConnectorsAngles[0];
                else
                    nextInterval = shprossConnector.AllowedConnectorsAngles[i + 1];

                AngleInterval currentInterval = new AngleInterval(interval.MiddleAngle, nextInterval.MiddleAngle);
                if (currentInterval.IsAngleInInterval(referencePointAngle))
                    return currentInterval.Angle;
            }
            throw new Exception("Угол не был найден.");
        }

    }
}
